/*
 *  Box.cpp
 *  Cheeky Dice
 *
 *  Created by Dmitry Fadeev on 15.03.11.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */

#include "Box.h"
#include <xml/Stream.h>

namespace liba
{
namespace lib3d
{
namespace node
{
	
const Atom Box::node_name("Box");
const Atom Box::shaders_name("Shader");
	
Box::Box(const Atom & name, Node * parent) : Node(name, parent), shaders(shaders_name)
{
	node_type = node_name;
	hard::Manager::add_dependent(this);
}

Box::~Box()
{
	hard::Manager::remove_dependent(this);
}

Box * Box::Clone()
{
	Box * new_box = new Box(*this);
	hard::Manager::add_dependent(new_box);
	return new_box;
}
	
void Box::on_add_hardware(hard::Manager::hardware_type * ha)
{
	hard_vector.push_back(SweetPair(ha, new PrimitiveObject(ha->create_vertex_buffer(), ha->create_index_buffer())));
}

bool Box::on_node(xml::Provider * prov, const std::string & name)
{
	if (name == "Shaders")
		return prov->parse_object(&shaders);
	if (name == "Shader")
	{
		std::auto_ptr<material::Shader> localShader(new material::Shader());
		if (!prov->parse_object(localShader.get()))
			return false;

		this->shaders.push_back(localShader.release());
		return true;
	}
	
	return Node::on_node(prov, name);
}
	
void Box::create()
{
	/*
	
	    5---------6
	   /|        /|
	  / |       / |
	 1---------2  |
	 |  |      |  |
	 |  4------|--7
	 | /       | /
	 |/        |/
	 0---------3
	 
	 */
	// box data
	static float box_corners[] = { -1.0f, -1.0f, -1.0f,   // 0
								   -1.0f,  1.0f, -1.0f,   // 1
									1.0f,  1.0f, -1.0f,   // 2
									1.0f, -1.0f, -1.0f,   // 3
								   -1.0f, -1.0f,  1.0f,   // 4
								   -1.0f,  1.0f,  1.0f,   // 5
									1.0f,  1.0f,  1.0f,   // 6
									1.0f, -1.0f,  1.0f }; // 7
	
	static float box_normals[] = {  0.0f,  0.0f, -1.0f,   // front
								   -1.0f,  0.0f,  0.0f,   // left
									0.0f,  0.0f,  1.0f,   // back
									1.0f,  0.0f,  0.0f,   // right
									0.0f,  1.0f,  0.0f,   // top
									0.0f, -1.0f,  0.0f }; // bottom
	
	static float box_uvs[]     = { 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
	
	static int box_map[]       = { 0, 1, 2, 3, 
								   4, 5, 1, 0, 
								   7, 6, 5, 4, 
								   3, 2, 6, 7, 
								   1, 5, 6, 2, 
								   4, 0, 3, 7 };
	
	// create arrays for vertex and index buffers data
	SimpleArray<float> positions(6 * 4 * 3);
	SimpleArray<float> normals(6 * 4 * 3);
	SimpleArray<float> uvs(6 * 4 * 2);
	SimpleArray<int>   indices(6 * 2 * 3);
	
	// getting pointers to arrays data
	float * positions_data = positions.data();
	float * normals_data   = normals.data();
	float * uvs_data       = uvs.data();
	
	// fill arrays with data
	for(int i = 0; i < 6; i++) // sides
	{
		for(int j = 0; j < 4; j++) // corners
		{
			for(int k = 0; k < 3; k++) // vertor elements
			{
				*positions_data++ = box_corners[box_map[i * 4 + j] * 3 + k];
				*normals_data++   = box_normals[i * 3 + k];
				if(k < 2)
					*uvs_data++ = box_uvs[j * 2 + k];
			}
		}
		// add indices
		// first triangle
		indices[i * 6 + 0] = i * 4 + 0;
		indices[i * 6 + 1] = i * 4 + 1;
		indices[i * 6 + 2] = i * 4 + 2;
		
		// second triangle
		indices[i * 6 + 3] = i * 4 + 2;
		indices[i * 6 + 4] = i * 4 + 3;
		indices[i * 6 + 5] = i * 4 + 0;
	}
	
	// send data to buffers
	SimpleArray<float> empty_float;
	HardVector::iterator it = hard_vector.begin();
	for(; it != hard_vector.end(); ++it)
	{
		// getting object
		PrimitiveObject * object = it->second;
		
		// update vertex buffer
		if(!object->vb->update(positions, normals, empty_float, uvs, uvs))
		{
			THROW(Gluck(__FUNCTION__, "vb->update() failed"));
		}
		
		// update index buffer
		if(!object->ib->update(indices.data(), indices.size()))
		{
			THROW(Gluck(__FUNCTION__, "vb->update() failed"));
		}
	}
}
	
void Box::RenderToList(hard::Hardware * hardware, hard::RenderList * render_list)
{
	// getting object and check if it valid
	PrimitiveObject * object = find_data(hardware);
	if(!object->vb->is_valid() || !object->ib->is_valid()) create();
	
	// render all materials
	material::Shaders::const_iterator it = shaders.begin();
	int primitive_position = 0;
	int vertex_position    = 0;
	for(; it != shaders.end(); ++it)
	{
		// skip transparent material
		if((*it)->get_transparency() > 0.99)
			continue;
		
		// getting material data
		int primitive_count = (*it)->get_triangles_count();
		int vertex_count    = (*it)->get_vertex_count();
		
		// fill render model
		hard::RenderModel render_model;
		render_model.world           = GetGlobalMatrix();
		render_model.vb              = object->vb;
		//render_model.bounding_sphere = get_rep()->get_bounding_sphere();
		render_model.primitive_start = 0;
		render_model.primitive_count = 12;
		render_model.ib              = object->ib;
		render_model.vertex_start    = 0;
		render_model.vertex_count    = 24;

		// add to render list
		(*it)->render_to_list(hardware, &render_model.material);
		render_list->models.push_back(render_model);
		
		// update positions
		primitive_position += primitive_count * 3;
		vertex_position    += vertex_count;
	}
}

}
}
}